#include "HObject.h"
#include "LinkedList.h"

namespace helix
{

	ListNode::ListNode()
	{
		obj=NULL;
		next=NULL;
	}

	ListNode::ListNode(HObject* object)		//initializes obj to object
	{
		obj=object;
		next=NULL;
	}

	ListNode::ListNode(HObject* object, ListNode* nextObj)
	{
		obj=object;
		next=nextObj;
	}

	//it is up to the list class to take care of poiners
	ListNode::~ListNode()
	{
		obj=NULL;
		next=NULL;
	}

	////////////////List Class//////////////////////
	LinkedList::LinkedList()
	{
		length=0;
		position=-1;
		head.next=NULL;
		head.obj=NULL;
		curNode=NULL;
	}

	LinkedList::~LinkedList()
	{
		Log().Println("Beginning of linked list deinitialization...");
		if(length<1)
		{
			Log().Println("...List is empty");
		}

		else
		{
			First();		//set the position of curObject to first
			while(length)
			{
				Delete();		//remove all objects from the list
			}
		}
		Log().Println("...End of linked list deinitialization");
	}

	void LinkedList::AddFirst(HObject* obj)	//adds a link to the object at the beginning of the list
	{
		obj->AddRef();
		ListNode *newNode=new ListNode(obj);
		if(length<=0)
		{
			head.next=newNode;
			newNode->next=NULL;
		}

		else
		{
			newNode->next=head.next;
			head.next=newNode;
		}

		length++;
		position=0;
		curNode=newNode;
	}

	void LinkedList::AddMiddle(HObject* obj,int pos)	//adds a link to an arbitrary position in the list
	{
		if(length==0 || pos<=0)
			AddFirst(obj);

		else if(pos>=length)
			AddLast(obj);

		else
		{
			obj->AddRef();
			if(pos<=position)
			{
				curNode=head.next;
				position=0;
			}

			while(position<pos-1)	//loop through the list until we find the position right before the one we want
			{
				curNode=curNode->next;
				position++;
			}

			ListNode *newNode=new ListNode(obj);
			newNode->next=curNode->next;
			curNode->next=newNode;
			curNode=newNode;
			position++;
			length++;
		}
	}
				
	void LinkedList::AddLast(HObject* obj)		//adds a link to the object at the end of the list
	{
		if(length<1)
		{
			AddFirst(obj);
		}
		else
		{
			obj->AddRef();

			while(position<length-1)				//find the last object in the list
			{
				curNode=curNode->next;
				position++;
			}

			ListNode *newNode=new ListNode(obj);
			
			curNode->next=newNode;
			curNode=newNode;
			position++;
			length++;
		}
	}

	void LinkedList::Delete(int position)		//deletes the object and the link to the object in the specified position
	{
		HObject *obj=GetObj(position);
		Remove();
		if(obj)
		{
			if(obj->GetRefCount()<=0)
			{
				delete obj;
				obj=NULL;
			}
		}
	}

	void LinkedList::Delete(HObject *obj)      //search for a specific object and delete it
	{
		HObject *rmObj=Remove(obj);
		if(rmObj)
		{
			if(rmObj->GetRefCount()<=0)
			{
				delete rmObj;
				rmObj=NULL;
			}
		}
	}

	void LinkedList::Delete()					//delete the current object
	{
		HObject *obj=Remove();
		if(obj)
		{
			if(obj->GetRefCount()<=0)
			{
				delete obj;
				obj=NULL;
			}
		}
	}

	int LinkedList::Length()
	{
		return length;
	}

	HObject* LinkedList::Remove()			//remove the current object from the list
	{
		if(length<1 || curNode==NULL)
			return NULL;

		//special case if the length == 1
		if(length==1)
		{
			HObject *obj=head.next->obj;
			delete head.next;
			curNode=NULL;
			head.next=NULL;
			length=0;
			position=-1;
			if(obj)
				obj->DeleteRef();
			return obj;
		}

		//get the object that the current node is pointing to
		HObject *obj=curNode->obj;

		ListNode *temp=head.next;
		if(position==0)
		{
			//point the temp node at the second node in the list
			//point the head.next node at the second node in the list (the first node is effectively removed)
			temp=head.next->next;
			head.next=temp;
		}
		else
		{
			while(temp->next!=curNode)
			{
				temp=temp->next;
			}
			temp->next=temp->next->next;
		}

		//delete the first node
		delete curNode;
		//decrease the length of the list by one
		length--;

		//check to see if this is the first node
		if(position==0)
		{
			//then have the current node point to it
			curNode=temp;
		}
		//otherwise, if there is another object after the one that was deleted
		else if(temp->next)
		{
			//then point the current node at it
			curNode=temp->next;
		}
		//otherwise, (it is the last node)
		else
		{
			curNode=temp;
			position--;
		}

		if(length==0)
		{
			position=-1;
			curNode=NULL;
		}

		obj->DeleteRef();
		return obj;
	}

	HObject* LinkedList::Remove(HObject *obj)	//search and remove object
	{
		if(length<1)
			return NULL;
		
		curNode=head.next;
		position=0;
		if(obj==curNode->obj)		//if it's the first object in the list
		{
			return Remove();		//remove it
		}

		do							//otherwise, search for it in the list
		{
			curNode=curNode->next;
			position++;
		}while(position<length && curNode->obj!=obj);	//if we found it or reached the end of the list, stop

		if(curNode->obj==obj)		//if we found it
			return Remove();		//remove it

		return NULL;				//othewise remove nothing and return NULL
	}

	HObject* LinkedList::GetObj(int pos)	//returns the object in the specified position
	{
		if(length<1)
			return NULL;

		if(pos>length)
			pos=length;

		if(pos<position)	//if the current node is after this position
		{	
			position=0;			//set it to the first element
			curNode=head.next;
		}

		while(position<pos)
		{
			curNode=curNode->next;
			position++;
		}

		return curNode->obj;
	}

	bool LinkedList::Contains(HObject *obj)
	{
		ListNode *node=head.next;
		while(node->obj!=obj && node->next!=NULL)
		{
			if(node->obj==obj)
				return true;
		}

		return false;
	}

	int LinkedList::IndexOf(HObject *obj)
	{
		ListNode *node=head.next;
		int i=0;
		while(node->obj!=obj && node->next!=NULL)
		{
			if(node->obj==obj)
				return i;
			i++;
		}

		return -1;
	}


	HObject* LinkedList::GetObj()				//returns the current object
	{
		if(length<1)
			return NULL;

		return curNode->obj;
	}

	HObject* LinkedList::Next()					//move to the next object
	{
		if(length<1 || position==length-1)
			return NULL;

		position++;
		curNode=curNode->next;

		return curNode->obj;
	}

	HObject* LinkedList::First()				//set the position and return the first object
	{
		if(length<1)
			return NULL;

		position=0;
		curNode=head.next;
		return curNode->obj;
	}

	void LinkedList::Print()			//calls all the objects print functions and prints them to the log file
	{
		Log().Println("Linked List-%d objects, curPos=%d",length,position);

		ListNode *tmp=head.next;
		int i=0;
		while(tmp!=NULL)
		{
			Log().Print("Object %d: ",i);
			if(tmp->obj)
				tmp->obj->Print();
			tmp=tmp->next;
		}
	}

}